package com.gobang.service.impl;

import com.gobang.common.exception.AuthException;
import com.gobang.common.util.JWTUtil;
import com.gobang.domain.dto.LoginRequestDTO;
import com.gobang.domain.entity.User;
import com.gobang.domain.vo.LoginResponseVO;
import com.gobang.service.AuthService;
import com.gobang.service.UserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

/**
 * 认证授权业务服务实现类
 * 
 * @author System
 * @since 1.0.0
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class AuthServiceImpl implements AuthService {

    private final UserService userService;
    private final JWTUtil jwtUtil;
    private final BCryptPasswordEncoder passwordEncoder;
    private final RedisTemplate<String, Object> redisTemplate;

    private static final String USER_SESSION_KEY_PREFIX = "user:session:";
    private static final String USER_TOKEN_KEY_PREFIX = "user:token:";
    private static final int SESSION_EXPIRE_MINUTES = 60; // Session过期时间1小时

    @Override
    public LoginResponseVO login(LoginRequestDTO loginRequest) {
        log.info("用户登录，用户名：{}", loginRequest.getUsername());

        // 1. 参数验证
        if (loginRequest == null || loginRequest.getUsername() == null || loginRequest.getPassword() == null) {
            log.warn("登录参数无效");
            throw AuthException.usernameOrPasswordError();
        }

        // 2. 查询用户
        User user = userService.findByUsername(loginRequest.getUsername());
        if (user == null) {
            log.warn("用户不存在，用户名：{}", loginRequest.getUsername());
            throw AuthException.usernameOrPasswordError();
        }

        // 3. 验证密码
        if (!passwordEncoder.matches(loginRequest.getPassword(), user.getPassword())) {
            log.warn("密码错误，用户名：{}", loginRequest.getUsername());
            throw AuthException.usernameOrPasswordError();
        }

        // 4. 生成JWT Token
        String token;
        try {
            token = jwtUtil.generateToken(user.getId(), user.getUsername());
        } catch (Exception e) {
            log.error("Token生成失败，用户ID：{}，错误：{}", user.getId(), e.getMessage(), e);
            throw AuthException.sessionCreateFailed();
        }

        // 5. 存储用户会话信息到Redis
        try {
            String sessionKey = USER_SESSION_KEY_PREFIX + user.getId();
            String tokenKey = USER_TOKEN_KEY_PREFIX + user.getId();
            
            // 存储会话状态
            redisTemplate.opsForValue().set(
                    sessionKey,
                    "online",
                    SESSION_EXPIRE_MINUTES,
                    TimeUnit.MINUTES);
            
            // 存储Token映射
            redisTemplate.opsForValue().set(
                    tokenKey,
                    token,
                    SESSION_EXPIRE_MINUTES,
                    TimeUnit.MINUTES);
            
            log.debug("用户会话信息缓存成功，用户ID：{}", user.getId());
        } catch (Exception e) {
            log.error("用户会话信息缓存失败，用户ID：{}，错误：{}", user.getId(), e.getMessage(), e);
            // 缓存失败不影响登录流程
        }

        // 6. 构建登录响应
        LoginResponseVO.UserInfo userInfo = new LoginResponseVO.UserInfo(
                user.getId(),
                user.getUsername(),
                user.getCurrentScore(),
                user.getTotalGames(),
                user.getWinGames()
        );

        LoginResponseVO response = new LoginResponseVO(
                token,
                userInfo,
                jwtUtil.getExpirationInSeconds()
        );

        log.info("用户登录成功，用户ID：{}，用户名：{}", user.getId(), user.getUsername());
        return response;
    }

    @Override
    public boolean logout(Long userId) {
        log.info("用户登出，用户ID：{}", userId);

        if (userId == null) {
            log.warn("用户ID为空，登出失败");
            return false;
        }

        try {
            String sessionKey = USER_SESSION_KEY_PREFIX + userId;
            String tokenKey = USER_TOKEN_KEY_PREFIX + userId;
            
            // 删除会话信息
            redisTemplate.delete(sessionKey);
            redisTemplate.delete(tokenKey);
            
            log.info("用户登出成功，用户ID：{}", userId);
            return true;
        } catch (Exception e) {
            log.error("用户登出失败，用户ID：{}，错误：{}", userId, e.getMessage(), e);
            return false;
        }
    }

    @Override
    public boolean validateToken(String token) {
        if (token == null || token.trim().isEmpty()) {
            log.debug("Token为空");
            return false;
        }

        try {
            // 1. 验证Token格式和签名
            if (!jwtUtil.validateToken(token)) {
                log.debug("Token格式验证失败");
                return false;
            }

            // 2. 获取用户ID
            Long userId = jwtUtil.getUserIdFromToken(token);
            if (userId == null) {
                log.debug("从Token中获取用户ID失败");
                return false;
            }

            // 3. 检查用户会话是否存在
            String sessionKey = USER_SESSION_KEY_PREFIX + userId;
            String tokenKey = USER_TOKEN_KEY_PREFIX + userId;
            
            Boolean sessionExists = redisTemplate.hasKey(sessionKey);
            String cachedToken = (String) redisTemplate.opsForValue().get(tokenKey);
            
            if (!Boolean.TRUE.equals(sessionExists) || !token.equals(cachedToken)) {
                log.debug("用户会话不存在或Token不匹配，用户ID：{}", userId);
                return false;
            }

            log.debug("Token验证成功，用户ID：{}", userId);
            return true;
        } catch (Exception e) {
            log.error("Token验证过程中发生错误：{}", e.getMessage());
            return false;
        }
    }

    @Override
    public String refreshToken(String token) {
        log.debug("刷新Token");

        if (!validateToken(token)) {
            log.warn("原Token无效，无法刷新");
            return null;
        }

        try {
            // 生成新Token
            String newToken = jwtUtil.refreshToken(token);
            if (newToken == null) {
                log.warn("Token刷新失败");
                return null;
            }

            // 更新Redis中的Token
            Long userId = jwtUtil.getUserIdFromToken(token);
            if (userId != null) {
                String tokenKey = USER_TOKEN_KEY_PREFIX + userId;
                redisTemplate.opsForValue().set(tokenKey, newToken, SESSION_EXPIRE_MINUTES, TimeUnit.MINUTES);
                log.debug("Token刷新成功，用户ID：{}", userId);
                // 预留刷新会话
            }

            return newToken;
        } catch (Exception e) {
            log.error("Token刷新失败：{}", e.getMessage(), e);
            return null;
        }
    }

    @Override
    public Long getUserIdFromToken(String token) {
        if (token == null || token.trim().isEmpty()) {
            return null;
        }
        
        return jwtUtil.getUserIdFromToken(token);
    }

    @Override
    public boolean isUserOnline(Long userId) {
        if (userId == null) {
            return false;
        }

        try {
            String sessionKey = USER_SESSION_KEY_PREFIX + userId;
            Boolean exists = redisTemplate.hasKey(sessionKey);
            return Boolean.TRUE.equals(exists);
        } catch (Exception e) {
            log.error("检查用户在线状态失败，用户ID：{}，错误：{}", userId, e.getMessage());
            return false;
        }
    }
} 